﻿using System;
using System.Threading;
using System.Windows.Forms;

namespace Krile
{
    class Program : ApplicationContext
    {

        #region Entry point
        /// <summary>
        /// Application entry point
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Program());
        }
        #endregion

        #region Global constructor and destructor

        private Program()
        {
            //Global constructor
            
            //Set application global event handler
            Application.ApplicationExit += new EventHandler(Exit);
#if !DEBUG
            Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
#endif

            #region Splashscreen init
            Forms.Dialogs.Popup.Splash splash = null;
            if (System.IO.File.Exists(
                System.IO.Path.Combine(Application.StartupPath, Define.SplashConfigFileName)))
            {
                splash = new Krile.Forms.Dialogs.Popup.Splash();
                splash.Show();
                splash.Refresh();
            }
            Application.DoEvents();
            #endregion

            Core.Init();

            #region Security init
            if (Core.Config.Security.StrictSecurity)
            {
                if (!Subsystem.Security.SecureStart())
                {
                    Application.Exit();
                    return;
                }
            }
            else
            {
                Subsystem.Security.DefaultStart();
            }
            #endregion

            Subsystem.SetGlobal.SetRenderer();

            #region Firstboot introduction
#if ! DEBUG
            if (Core.Config.IsFirstboot)
            {
                using (Forms.Dialogs.Show.Firstboot first = new Krile.Forms.Dialogs.Show.Firstboot())
                {
                    first.ShowDialog();
                }
            }
#endif
            #endregion

            Core.FormManager.CreateForms();
            Core.KeyAssign.SetKeyEvent(Core.FormManager.MainForm);
            Core.SetLibBridge();
            Core.PluginManager.LoadPlugins();
            //State restore
            Core.Config.LoadState();
            Core.KeyAssign.DefreezeAssigns();
            #region Prepare for system start
            Core.FormManager.TrayForm.Init();

            Core.PluginManager.StartPlugin();
            #endregion

            #region Firstboot config
            if (Core.Config.IsFirstboot)
            {
                using (var cfg = new Forms.ConfigForm.Config())
                {
                    cfg.ShowDialog();
                }
            }
            #endregion

            if (Core.Config.Behavior.ShowMainWindowOnStartup)
                Core.FormManager.MainForm.Show();

            #region Splashscreen dispose

            if (splash != null)
            {
                splash.Close();
                splash.Dispose();
            }

            #endregion
        }

        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            try
            {
                string destination = System.IO.Path.Combine(Application.StartupPath, System.IO.Path.GetRandomFileName() + ".txt");
                try
                {
                    Exception excp = (Exception)e.ExceptionObject;
                    using (System.IO.StreamWriter sw = new System.IO.StreamWriter(destination))
                    {
                        sw.WriteLine("**KRILE FATAL ERROR REPORT**");
                        sw.WriteLine(DateTime.Now.ToString() + " @ " + Environment.OSVersion.VersionString);
                        sw.WriteLine(Environment.NewLine + ">>BODY<<");
                        sw.WriteLine(excp.ToString());
                        sw.WriteLine(Environment.NewLine + ">>BASE<<");
                        sw.WriteLine(excp.GetBaseException());
                    }
                }
                finally
                {
                    MessageBox.Show(
        @"Sorry, FATAL ERROR WAS OCCURED.
Krile will be killing itself soon.
If you can spare your time to Krile, please report detail file to author.
Report file is here:" + destination, "Krile fatal error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
                }
            }
            finally
            {
                Application.Exit();
            }
        }

        void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            if (e.Exception is KrileStrictCheckException)
            {
                MessageBox.Show(e.Exception.Message, "Strict Check Failured.", MessageBoxButtons.OK, MessageBoxIcon.Hand);
            }
            else if (e.Exception is ObjectDisposedException)
            {
                //Disposing error
            }
            else
            {
                #region ExceptionDialog
                using (var ex = new Forms.Dialogs.Show.ExceptionTracking(e.Exception))
                {
                    var result = ex.ShowDialog();
                    if (result == DialogResult.Abort)
                    {
                        try
                        {
                            Environment.Exit(-1);
                        }
                        catch
                        {
                            Environment.FailFast("Unrecoveriable error occured.");
                        }
                    }
                    else if (result == DialogResult.Retry)
                    {
                        Application.Restart();
                    }
                }
                #endregion
            }
        }

        void Exit(object sender, EventArgs e)
        {
            //Global destructor
            Subsystem.Debugger.WriteReport(false);
            this.Dispose(true);
        }

        #endregion
    }
}